#include "server.h"

static MONSVC_FUNC(HELP);
static MONSVC_FUNC(PRINTCLIENT);
static MONSVC_FUNC(PRINTSERVICE);

static struct _monsvc_map
{/*{{{*/
    char *name;
    char *ab;
    P_MONSVC_FUNC p_func;
    char *desc;
    char *usage;
}/*}}}*/
monsvc_map[]=
{/*{{{*/
    {
        "HELP", "HELP", MONSVC_FUNC_HELP,
        "",
        ""
    },
    {
        "PRINTCLIENT", "PCLT", MONSVC_FUNC_PRINTCLIENT,
        "print client ip & port",
        "printclient(pclt)"
    },
    {
        "PRINTSERVICE", "PSVC", MONSVC_FUNC_PRINTSERVICE,
        "print service status",
        "printservice(psvc)"
    }
};/*}}}*/
static const unsigned int MAPSIZE=
sizeof(monsvc_map)/sizeof(struct _monsvc_map);

static MONSVC_FUNC(HELP)
{/*{{{*/
    struct _monsvc_map *map=NULL;
    int offset=0;
    int i;

    // 所有帮助
    if(body)
        while(*body == SPACE)
            body++;
    if(!body || BUF_IS_CLR(body))
    {
        offset+=sprintf(msg_out+offset, "help [command]\n");
        offset+=sprintf(msg_out+offset, "commands:\n");
        for(i=1; i<MAPSIZE; ++i)
            offset+=sprintf(msg_out+offset, "    %s\n",
                    monsvc_map[i].name);
        return(offset);
    }

    // 单独命令帮助
    for(i=1; i<MAPSIZE; ++i)
    {
        map=&monsvc_map[i];
        if(strcasecmp(map->name, body) == 0 ||
                strcasecmp(map->ab, body) == 0)
        {
            offset+=sprintf(msg_out+offset, "Description: %s\n",
                    map->desc);
            offset+=sprintf(msg_out+offset, "Usage: %s\n",
                    map->usage);
            return(offset);
        }
    }

    offset+=sprintf(msg_out+offset, "Command not found, type help\n");
    return(offset);
}/*}}}*/

static MONSVC_FUNC(PRINTCLIENT)
{/*{{{*/
    int offset=0;
    struct _worker_info *worker_info;
    struct _client_info *client_info;
    int client_num=0;
    char buf[50];
    int i, j;

    offset+=sprintf(msg_out+offset,
            "Client                         Million        Count\n");
    offset+=sprintf(msg_out+offset,
            "-------                        -------        -------\n");
    pthread_mutex_lock(&share_info.worker_info_lock);
    for(i=0; i<WORKER_NUM; ++i)
    {
        worker_info=&share_info.worker_info[i];
        for(j=0; j<EP_POLL_MAX_NUM; ++j)
        {
            client_info=&worker_info->client_info[j];
            if(client_info->ip)
            {
                sprintf(buf, "%s:%d",
                        client_info->ip, client_info->port);
                offset+=sprintf(msg_out+offset,
                        "%-30s %-6d         %d\n",
                        buf, client_info->loop, client_info->count);
            }
        }
        client_num+=worker_info->client_num;
    }
    pthread_mutex_unlock(&share_info.worker_info_lock);
    offset+=sprintf(msg_out+offset,
            "-----------\n"
            "Total: %d",
            client_num);

    return(offset);
}/*}}}*/

static MONSVC_FUNC(PRINTSERVICE)
{/*{{{*/
    int offset=0;
    struct _svc_info *info;
    int i;

    offset+=sprintf(msg_out+offset,
            "Service                        Million        Count\n");
    offset+=sprintf(msg_out+offset,
            "-------                        -------        -------\n");
    pthread_mutex_lock(&share_info.svc_info_lock);
    for(i=0; i<share_info.svc_num; ++i)
    {
        info=&share_info.svc_info[i];
        offset+=sprintf(msg_out+offset,
                "%-30s %-6d         %d\n",
                info->name, info->loop, info->count);
    }
    pthread_mutex_unlock(&share_info.svc_info_lock);

    return(offset);
}/*}}}*/

static int monsvc_call(char *msg_in, char *msg_out)
{/*{{{*/
    int offset=0;
    struct _monsvc_map *map=NULL;
    char *body;
    int svcsize;

    body=strchr(msg_in, SPACE);
    if(body)
    {
        svcsize=body-msg_in;
        while(*body == SPACE) body++;
    }
    else
        svcsize=strlen(msg_in);
    if(svcsize > 0 && svcsize <= NAME_SIZE)
    {
        char svc[NAME_SIZE+1];
        int i;

        strncpy(svc, msg_in, svcsize);
        svc[svcsize]=ZERO; 
#ifdef _MY_DEBUG
        errlog("%s: svc(%s)", __func__, svc);
#endif
        for(i=0; i<MAPSIZE; ++i)
        {
            map=&monsvc_map[i];
            if(strcasecmp(map->name, svc) == 0 ||
                    strcasecmp(map->ab, svc) == 0)
                break;
        }
        if(i == MAPSIZE)
        {
            offset+=sprintf(msg_out+offset, "command(%s) not found",
                    svc);
            errlog("%s %s: %s", __FILE__, __func__, msg_out);
            offset+=sprintf(msg_out+offset, "\n");
        }
        else
        {
            offset+=map->p_func(body, msg_out);
        }
    }
    else
    {
        offset+=sprintf(msg_out+offset, "command invalid");
        errlog("%s %s: %s", __FILE__, __func__, msg_out);
        offset+=sprintf(msg_out+offset, "\n");
    }
    offset+=sprintf(msg_out+offset, TEXTEND);

    return offset;
}/*}}}*/

int thrd_monitor(struct monitor_ctl *monitor_ctl)
{/*{{{*/
#define _TIMEOUT        5
#define _TIMEOUT_MS     (_TIMEOUT*1000) 
    char *msg_in=monitor_ctl->msg_in;
    int msg_in_size;
    char *msg_out=monitor_ctl->msg_out;
    int msg_out_size;
    SKT term_skt;
    int res;
    int timeout_ms;
    int accu_timeout;

    while(1)
    {
        timeout_ms=_TIMEOUT_MS;
        if((res=skt_accept(monitor_ctl->skt, &timeout_ms, &term_skt)))
        {
            if(res != RET_ERR_TIMEOUT)
                errlog("%s %s: accept fail", __FILE__, __func__);
            continue;
        }
#ifdef _MY_DEBUG
        errlog("%s %s: console login", __FILE__, __func__);
#endif

        accu_timeout=0;
        while(1)
        {
            timeout_ms=_TIMEOUT_MS;
            if((res=skt_read(term_skt,
                            (unsigned char *)msg_in, &msg_in_size,
                            SKT_R_LINE, &timeout_ms)) < 0)
            {
                if(res == RET_ERR_TIMEOUT)
                {
                    accu_timeout+=_TIMEOUT;
                    if(accu_timeout < MONITOR_IDLE_TIMEOUT)
                        continue;
                    errlog("%s %s: console timeout, revoke",
                            __FILE__, __func__);
                }
                else
                {
                    if(res == RET_ERR_ABORT)
                        errlog("%s %s: console revoke",
                                __FILE__, __func__);
                    else
                        errlog("%s %s: read console fail, res:%d, revoke",
                                __FILE__, __func__, res);
                }
                skt_close(term_skt);
                break;
            }
            if(msg_in[msg_in_size-1] == RTN)
            {
                if(msg_in[msg_in_size-2] == '\r')
                    msg_in[msg_in_size-2]=ZERO;
                else
                    msg_in[msg_in_size-1]=ZERO;
            }
            else
                msg_in[msg_in_size]=ZERO;
            trim_head_space(msg_in);
            trim_tail_space(msg_in);
            accu_timeout=0;

            msg_out_size=monsvc_call(msg_in, msg_out);

            timeout_ms=_TIMEOUT_MS;
            if((res=skt_write(term_skt,
                            (unsigned char *)msg_out, &msg_out_size,
                            SKT_W_SIZE, &timeout_ms)) < 0)
            {
                errlog("%s %s: respond to console fail, res:%d, revoke",
                        __FILE__, __func__, res);
                skt_close(term_skt);
                break;
            }
        } // console
    }

    return(0);
}/*}}}*/
